
#include "bcrypt.hpp"
#include <cstdlib>
#include <sstream>
#include <iomanip>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>
#include <openssl/buffer.h>

namespace {

// Base64 编码
std::string base64_encode(const unsigned char* input, int length) {
    BIO *bmem = nullptr, *b64 = nullptr;
    BUF_MEM *bptr = nullptr;

    b64 = BIO_new(BIO_f_base64());
    bmem = BIO_new(BIO_s_mem());
    // Do not use newlines to flush buffer
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    b64 = BIO_push(b64, bmem);
    BIO_write(b64, input, length);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);

    std::string output(bptr->data, bptr->length);
    BIO_free_all(b64);

    return output;
}

std::string sha256(const std::string& str) {
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const unsigned char*>(str.c_str()), str.length(), hash);
    return base64_encode(hash, SHA256_DIGEST_LENGTH);
}

std::string generateSalt(int length = 16) {
    unsigned char salt[16];
    if (!RAND_bytes(salt, sizeof(salt))) {
        throw std::runtime_error("Failed to generate random salt");
    }
    return base64_encode(salt, length);
}

} 

namespace bcrypt {

std::string generateHash(const std::string& password, int cost) {
    std::string salt = generateSalt();
    std::string hash = sha256(salt + password);
    return salt + "$" + hash;
}

bool validatePassword(const std::string& password, const std::string& hash) {
    size_t pos = hash.find('$');
    if (pos == std::string::npos) return false;

    std::string salt = hash.substr(0, pos);
    std::string originalHash = hash.substr(pos + 1);

    std::string checkHash = sha256(salt + password);
    return originalHash == checkHash;
}

}
